#include "stdafx.h"
#include "ZSocket.h"
#include "ZUtility.h"
#include <mstcpip.h>
#include <assert.h>

namespace	Z	{

//------------------------------------------------------------------------------------------------------------------------
Socket::Socket(SOCKET so)
:	mHandle(so)
{
}

Socket::~Socket()
{
	close();
}

bool Socket::create(int af, int type, int protocol, DWORD flags)
{
	assert(mHandle == INVALID_SOCKET);

	mHandle	= ::WSASocket(af, type, protocol, 0, 0, flags);
	if(mHandle == INVALID_SOCKET)	{
		return false;
	}
	return setReuse();
}

void Socket::close()
{
	closeSocket(mHandle);
}

bool Socket::receive(LPWSABUF wsaBufs, DWORD wsaBufCount, LPWSAOVERLAPPED pOverlapped)
{
	DWORD bytesReceived	=0;
	DWORD flags	=0;
	const int error	= ::WSARecv(mHandle, wsaBufs, wsaBufCount, &bytesReceived, &flags, pOverlapped, 0);
	return !(error == SOCKET_ERROR && ::WSAGetLastError() != WSA_IO_PENDING);
}

bool Socket::send(LPWSABUF wsaBufs, DWORD wsaBufCount, LPWSAOVERLAPPED pOverlapped)
{
	DWORD bytesSent	= 0;
	const DWORD flags	= 0;
	const int error	= ::WSASend(mHandle, wsaBufs, wsaBufCount, &bytesSent, flags, pOverlapped, 0);
	return !(error == SOCKET_ERROR && ::WSAGetLastError() != WSA_IO_PENDING);
}

bool Socket::connect(const std::string& address, u_short port)
{
	SOCKADDR_IN sin;
	if(!Socket::getSockAddr(sin, address, port))
		return false;
	return connect(sin);
}

bool Socket::connect(SOCKADDR_IN& sin)
{
	u_long async	= 1;
	if(::ioctlsocket(mHandle, FIONBIO, &async) == SOCKET_ERROR)	{
		return false;
	}
	const int error	= ::connect(mHandle, (LPSOCKADDR)&sin, sizeof(sin));
	if(error == SOCKET_ERROR && ::WSAGetLastError( ) != WSAEWOULDBLOCK)	{
		return false;
	}

	fd_set fdset;
	FD_ZERO(&fdset);
	FD_SET(mHandle, &fdset);
	timeval timeout;
	timeout.tv_sec	= 3;
	timeout.tv_usec	= 0;
	if(::select(0, 0, &fdset, 0, &timeout) == SOCKET_ERROR)	{
		return false;
	}
	if(!FD_ISSET(mHandle, &fdset))	{
		return false;
	}

	async	= 0;
	if(::ioctlsocket(mHandle, FIONBIO, &async) == SOCKET_ERROR)	{
		return false;
	}

	return true;
}

bool Socket::setReuse()
{
	assert(mHandle != INVALID_SOCKET);

	const int reuse	= 1;
	return ::setsockopt(mHandle, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) != SOCKET_ERROR;
}

bool Socket::setSendBufferSize(int kSize)
{
	assert(mHandle != INVALID_SOCKET);

	return ::setsockopt(mHandle, SOL_SOCKET, SO_SNDBUF, (const char*)&kSize, sizeof(kSize)) != SOCKET_ERROR;
}

bool Socket::setKeepAlive(ms_t time, ms_t interval)
{
	assert(mHandle != INVALID_SOCKET);

	tcp_keepalive keepAlive;
	zeroise(keepAlive);
	keepAlive.onoff	= 1;
	keepAlive.keepalivetime	= time;
	keepAlive.keepaliveinterval	= interval;
	DWORD bytesReturned;
	return ::WSAIoctl(mHandle, SIO_KEEPALIVE_VALS, &keepAlive, sizeof(keepAlive), 0, 0, &bytesReturned, 0, 0) != SOCKET_ERROR;
}

bool Socket::setLinger(ms_t time)
{
	assert(mHandle != INVALID_SOCKET);

	LINGER linger;
	linger.l_onoff	= 1;
	linger.l_linger	= time;
	return ::setsockopt(mHandle, SOL_SOCKET, SO_LINGER, (const char*)&linger, sizeof(linger)) != SOCKET_ERROR;
}

bool Socket::getSockAddr(SOCKADDR_IN& sin, const std::string& address, u_short port)
{
	zeroise(sin);
	sin.sin_family	= AF_INET;
	sin.sin_addr.s_addr	= ::inet_addr(address.c_str());
	if(sin.sin_addr.s_addr == INADDR_NONE)	{
		LPHOSTENT lphost	= ::gethostbyname(address.c_str());
		if(lphost == 0)
			return false;
		sin.sin_addr.s_addr	= reinterpret_cast<LPIN_ADDR>(lphost->h_addr)->s_addr;
	}
	sin.sin_port	= ::htons(port);
	return true;
}

bool Socket::loadLib()
{
	WSADATA wsaData;
	const int error	= ::WSAStartup(0x0202, &wsaData);
	return error != SOCKET_ERROR;
}

void Socket::unloadLib()
{
	::WSACleanup();
}

}